import ply.yacc as yacc
from lex_pascal import tokens
from tabla_simbolos import *
import os.path

def p_parse_goal(p):
    '''parse_goal : program_file
                    | unit_file
                    | library_file
                    | package_file'''

    p[0] = p[1]

def p_error_recovery(p):
    '''error_recovery : INTERFACE
                    | IMPLEMENTATION
                    | VAR
                    | CONST
                    | TYPE
                    | RESOURCESTRING
                    | OBJECT
                    | FUNCTION
                    | PROCEDURE
                    | BEGIN
                    | END
                    | SEMICOLON'''

    p[0] = p[1]

def p_program_file(p):
    '''program_file : program_heading main_uses_clause program_block POINT'''

    p[0] = p[1],p[2],p[3]

def p_program_heading(p):
    '''program_heading :
                    | PROGRAM program_name program_heading_2'''

    if len(p) == 4:
        p[0] = p[2],p[3]
    else:
        p[0] = None

def p_program_heading_2(p):
    '''program_heading_2 : SEMICOLON
                    | ROUNDOPEN program_param_list ROUNDCLOSE SEMICOLON'''

    if len(p) == 2:
        p[0] = p[1]
    else:
        p[0] = p[2]

def p_program_name(p):
    '''program_name : IDENTIFIER'''

    p[0] = p[1]


def p_program_param_list(p):
    '''program_param_list : program_param
                    | program_param_list COMMA program_param'''

    if len(p) == 2:
        p[0] = p[1]
    else:
        p[0] = p[1],p[3]


def p_program_param(p):
    '''program_param : IDENTIFIER'''

    p[0] = p[1]

def p_program_block(p):         #La primer parte es para declaracion de var. const. etc., la segunda bloque ppal.
    '''program_block : program_decl_sect_list compound_stmt'''

    p[0] = p[1],p[2]


def p_program_decl_sect_list(p):
    '''program_decl_sect_list : impl_decl_sect_list'''

    p[0] = p[1]

def p_uses_clause(p):
    '''uses_clause :
                    | USES used_units_list SEMICOLON'''

    if len(p) == 4:
        p[0] = p[2]
    else:
        p[0] = None

def p_used_units_list(p):
    '''used_units_list : used_units_list COMMA used_unit_name
                    | used_unit_name'''

    if len(p) == 4:
        p[0] = p[1],p[3]
    else:
        p[0] = p[1]

def p_used_unit_name(p):
    '''used_unit_name : IDENTIFIER'''

    p[0] = p[1]

def p_main_uses_clause(p):
    '''main_uses_clause :
                    | USES main_used_units_list SEMICOLON'''

    if len(p) == 4:
        p[0] = p[2]
    else:
        p[0] = None

def p_main_used_units_list(p):
    '''main_used_units_list : main_used_units_list COMMA main_used_unit_name
                    | main_used_unit_name'''

    if len(p) == 4:
        p[0] = p[1],p[3]
    else:
        p[0] = p[1]

def p_main_used_unit_name(p):
    '''main_used_unit_name : IDENTIFIER
                    | IDENTIFIER IN STRINGLITERAL''' # VER #####################

    p[0] = p[1]

def p_library_file(p):
    '''library_file : library_heading main_uses_clause library_block POINT'''

    p[0] = p[1],p[2],p[3]

def p_library_heading(p):
    '''library_heading : LIBRARY IDENTIFIER SEMICOLON'''# VER ##################

    p[0] = p[1]

def p_library_block(p):
    '''library_block : library_impl_decl_sect_list compound_stmt'''

    p[0] = p[1],p[2]

def p_library_impl_decl_sect_list(p):
    '''library_impl_decl_sect_list :
                    | library_impl_decl_sect_list library_impl_decl_sect'''

    if len(p) == 3:
        p[0] = p[1],p[2]
    else:
        p[0] = None

def p_library_impl_decl_sect(p):
    '''library_impl_decl_sect : label_decl_sect
                    | const_decl_sect
                    | res_str_decl_sect
                    | type_decl_sect
                    | var_decl_sect
                    | proc_decl
                    | func_decl
                    | constructor_decl
                    | destructor_decl
                    | export_clause'''

    """XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"""
    #proc_decl terminado carga de simbolos, ver validacion semantica
    #func_decl terminado carga de simbolos

    p[0] = p[1]

def p_export_clause(p):
    '''export_clause : EXPORTS exports_list SEMICOLON'''

    p[0] = p[2]

def p_exports_list(p):
    '''exports_list : exports_entry
                    | exports_list COMMA exports_entry'''

    if len(p) == 4:
        p[0] = p[1],p[3]
    else:
        p[0] = p[1]

def p_exports_entry(p):
    '''exports_entry : identifier exports_index exports_name exports_resident'''

    p[0] = p[1],p[2],p[3],p[4]

def p_exports_index(p):
    '''exports_index :
                    | INDEX integer_const'''

    if len(p) == 3:
        p[0] = p[2]
    else:
        p[0] = None

def p_exports_name(p):
    '''exports_name :
                    | NAME identifier
                    | NAME literal'''

    if len(p) == 3:
        p[0] = p[2]
    else:
        p[0] = None

def p_exports_resident(p):
    '''exports_resident :
                    | RESIDENT'''

    if len(p) == 2:
        p[0] = p[1]
    else:
        p[0] = None

def p_unit_file(p):
    '''unit_file : unit_heading interface_part implementation_part initialization_part POINT'''

    p[0] = p[1],p[2],p[3],p[4]

def p_unit_heading(p):
    '''unit_heading : UNIT unit_name SEMICOLON'''

    p[0] = p[2]

def p_unit_name(p):
    '''unit_name : IDENTIFIER'''

    p[0] = p[1]

def p_interface_part(p):
    '''interface_part : INTERFACE uses_clause int_decl_sect_list'''

    p[0] = p[2], p[3]

def p_implementation_part(p):
    '''implementation_part : IMPLEMENTATION uses_clause impl_decl_sect_list'''

    p[0] = p[2], p[3]

def p_initialization_part(p):
    '''initialization_part : END
                    | INITIALIZATION stmt_list END
                    | INITIALIZATION stmt_list FINALIZATION stmt_list END
                    | BEGIN stmt_list END'''

    if len(p) == 2:
        p[0] = p[1]
    elif len(p) == 4:
        p[0] = p[2]
    else:
        p[0] = p[2],p[4]

def p_package_file(p):
    '''package_file : PACKAGE package_name SEMICOLON requires_clause contains_clause END POINT'''

    p[0] = p[2], p[4],p[5]

def p_package_name(p):
    '''package_name : identifier'''

    p[0] = p[1]

def p_requires_clause(p):
    '''requires_clause :
                    | REQUIRES
                    | REQUIRES main_used_units_list SEMICOLON'''

    if len(p) == 2:
        p[0] = p[1]
    elif len(p) == 4:
        p[0] = p[2]
    else:
        p[0] = None

def p_contains_clause(p):
    '''contains_clause :
                    | CONTAINS
                    | CONTAINS main_used_units_list SEMICOLON'''

    if len(p) == 2:
        p[0] = p[1]
    elif len(p) == 4:
        p[0] = p[2]
    else:
        p[0] = None

def p_int_decl_sect_lis(p):
    '''int_decl_sect_list :
                    | int_decl_sect_list int_decl_sect'''

    if len(p) == 3:
        p[0] = p[1],p[2]
    else:
        p[0] = None

def p_impl_decl_sect_list(p):
    '''impl_decl_sect_list :
                    | impl_decl_sect_list impl_decl_sect'''

    if len(p) == 3:
        p[0] = p[1],p[2]
    else:
        p[0] = None

def p_pint_decl_sect(p):
    '''int_decl_sect : const_decl_sect
                    | res_str_decl_sect
                    | type_decl_sect
                    | var_decl_sect
                    | int_proc_heading
                    | int_func_heading'''


def p_impl_decl_sect(p):                                                   #Declaraciones antes del BEGIN principal, hicimos VAR, hacer lo que falta
    '''impl_decl_sect : label_decl_sect
                    | const_decl_sect
                    | res_str_decl_sect
                    | type_decl_sect
                    | var_decl_sect
                    | proc_decl
                    | func_decl
                    | constructor_decl
                    | destructor_decl'''


def p_int_proc_heading(p):
    '''int_proc_heading : proc_heading
                    | proc_heading inline_directr SEMICOLON
                    | proc_heading FORWARD SEMICOLON
                    | proc_heading fp_directives'''

    if len(p) == 3:
        p[0] = p[1],p[2]
    elif len(p) == 2:
        p[0] = p[1]
    elif p[2] == 'inline_directr':
        p[0] = p[1],p[2]
    else:
        p[0] = p[1]

def p_int_func_heading(p):
    '''int_func_heading : func_heading
                    | func_heading inline_directr SEMICOLON
                    | func_heading FORWARD SEMICOLON
                    | func_heading fp_directives'''

    if len(p) == 3:
        p[0] = p[1],p[2]
    elif len(p) == 2:
        p[0] = p[1]
    elif p[2] == 'inline_directr':
        p[0] = p[1],p[2]
    else:
        p[0] = p[1]

def p_label_decl_sect(p):
    '''label_decl_sect : LABEL label_list SEMICOLON'''

    p[1] = p[2]

def p_label_list(p):
    '''label_list : label_name
                    | label_list COMMA label_name'''

    pass

def p_label_name(p):
    '''label_name : INT
                    | identifier'''

    pass                                            #No usamos LABEL

def p_const_decl_sect(p):
    '''const_decl_sect : CONST const_decl
                    | const_decl_sect const_decl'''

    if p[1] == 'const_decl_sect':
        p[0] = p[1],p[2]
    else:
        p[0] = p[2]

def p_res_str_decl_sect(p):
    '''res_str_decl_sect : RESOURCESTRING const_decl
                    | res_str_decl_sect const_decl'''

    if p[1] == 'res_str_decl_sect':
        p[0] = p[1],p[2]
    else:
        p[0] = p[2]

def p_type_decl_sect(p):
    '''type_decl_sect : TYPE type_decl
                    | type_decl_sect type_decl'''

    if p[1] == 'type_decl_sect':
        p[0] = p[1],p[2]
    else:
        p[0] = p[2]

    parser.ambito_actual = parser.ambito_actual.padre

def p_var_decl_sect(p):
    '''var_decl_sect : VAR var_decl
                    | THREADVAR var_decl
                    | var_decl_sect var_decl'''

    if p[1] == 'var_decl_sect':
        p[0] = p[1],p[2]
    else:
        p[0] = p[2]

def p_const_sect(p):
    '''const_decl : const_name EQUAL const_expr SEMICOLON
                    | const_name COLON type_ref EQUAL typed_const SEMICOLON'''

    if len(p) == 5:
        p[0] = p[1],p[3]
        for x in p[1]:
            const = constante()
            const.nombre = x

            try: # Intenta si es un string
                for x in p[3]:
                    for y in x:
                        if ((y >= 'A') & (y <= 'Z')) | ((y >= 'a') & (y <= 'z')):
                            break
                            #Le tengo que asignar el valor de la variable
                        else:
                            const.tipo = None
                            const.valor = p[3]
                            parser.ambito_actual.carga_simbolo(const)
                            break
                    break
            except:  # Sino es un integer
                const.tipo = None
                const.valor = p[3]
                parser.ambito_actual.carga_simbolo(const)
    else:
        p[0] = p[1],p[3],p[5]
        for x in p[1]:
            const = constante()
            const.nombre = x
            const.tipo = p[3]
            const.valor = p[5]
            parser.ambito_actual.carga_simbolo(const)


def p_const_name(p):
    '''const_name : identifier'''

    p[0] = [p[1]]

def p_const_expr(p):
    '''const_expr : const_simple_expr
                    | const_simple_expr const_relop const_simple_expr'''

    if len(p) == 2:
        p[0] = p[1]
    else:
        p[0] = p[1],p[2],p[3]

def p_const_relop(p):
    '''const_relop : EQUAL
                    | NOTEQUAL
                    | LOWER
                    | GREATER
                    | LOWEREQUAL
                    | GREATEREQUAL
                    | IN'''

    p[0] = p[1]

def p_const_simple_expr(p):
    '''const_simple_expr : const_term
                    | const_simple_expr const_addop const_term'''

    if len(p) == 2:
        p[0] = p[1]
    else:
        p[0] = p[1],p[2],p[3]

def p_const_addop(p):
    '''const_addop : PLUS
                    | MINUS
                    | OR
                    | XOR'''

    p[0] = p[1]

def p_const_term(p):
    '''const_term : const_factor
                    | const_term const_mulop const_factor'''

    if len(p) == 2:
        p[0] = p[1]
    else:
        p[0] = p[1],p[2],p[3]

def p_const_mulop(p):
    '''const_mulop : STAR
                | SLASH
                | DIV
                | MOD
                | SHL
                | SHR
                | AND'''

    p[0] = p[1]

def p_const_factor(p):
    '''const_factor : const_variable
                | const_set
                | unsigned_number
                | literal
                | NIL
                | ADDRESSOF const_factor
                | ROUNDOPEN const_expr ROUNDCLOSE
                | NOT const_factor
                | sign const_factor
                | DEREF const_factor'''

    if len(p) == 2:
        p[0] = p[1]
    elif (p[1] == 'sign'):
        p[0] = p[1],p[2]
    else:
        p[0] = p[2]

def p_const_set(p):
    '''const_set : SQUAREOPEN const_elem_list SQUARECLOSE'''

    p[0] = p[2]

def p_sign(p):
    '''sign : PLUS
                | MINUS'''

    p[0] = p[1]

def p_const_variable(p):
    '''const_variable : identifier
                | const_variable const_variable_2'''

    if len(p) == 2:
        p[0] = p[1]
    else:
        p[0] = p[1],p[2]

def p_const_variable_2(p):
    '''const_variable_2 : POINT identifier
                | DEREF
                | ROUNDOPEN const_func_expr_list ROUNDCLOSE'''

    if len(p) == 2:
        p[0] = p[1]
    else:
        p[0] = p[2]

def p_const_func_expr_list(p):
    '''const_func_expr_list : const_expr
                | const_expr COMMA const_expr'''

    if len(p) == 2:
        p[0] = p[1]
    else:
        p[0] = p[1], p[3]

def p_const_elem_list(p):
    '''const_elem_list :
                | const_elem_list1'''

    if len(p) == 2:
        p[0] = p[1]
    else:
        p[0] = None

def p_const_elem_list1(p):
    '''const_elem_list1 : const_elem
                | const_elem_list1 COMMA const_elem'''

    if len(p) == 2:
        p[0] = p[1]
    else:
        p[0] = p[1],p[3]

def p_const_elem(p):
    '''const_elem : const_expr
                | const_expr DOTDOT const_expr'''

    if len(p) == 2:
        p[0] = p[1]
    else:
        p[0] = p[1],p[3]

def p_unsigned_number(p):                        #INT es numero
    '''unsigned_number : INT
                | HEX
                | FLOAT'''

    p[0] = p[1]

def p_typed_const(p):
    '''typed_const : const_expr
                | array_const
                | record_const'''

    p[0] = p[1]

def p_array_const(p):
    '''array_const : ROUNDOPEN typed_const_list ROUNDCLOSE
                | ROUNDOPEN record_const ROUNDCLOSE
                | ROUNDOPEN array_const ROUNDCLOSE'''

    p[0] = p[2]

def p_typed_const_list(p):
    '''typed_const_list :
                | typed_const COMMA typed_const
                | typed_const_list COMMA typed_const'''

    if len(p) == 4:
        p[0] = p[1],p[3]
    else:
        p[0] = None

def p_record_const(p):
    '''record_const : ROUNDOPEN const_field_list ROUNDCLOSE'''

    p[0] = p[2]

def p_const_field_list(p):
    '''const_field_list : const_field SEMICOLON const_field_list
                | const_field SEMICOLON
                | const_field'''

    if len(p) == 3:
        p[0] = p[1]
    elif len(p) == 4:
        p[0] = p[1],p[3]
    else:
        p[0] = p[1]

def p_const_field(p):
    '''const_field : const_field_name COLON typed_const'''

    p[0] = p[1],p[3]

def p_const_field_name(p):
    '''const_field_name : identifier'''

    p[0] = p[1]

def p_type_decl(p):
    '''type_decl : identifier EQUAL type_decl_type SEMICOLON
                | error error_recovery'''

    if len(p) == 3:
        p[0] = p[1],p[2]
    else:
        p[0] = p[1],p[3]

    typ = typep()
    typ.nombre = p[1]
    typ.clase = "type"

    if p[3][0].upper() == 'RECORD': # TYPE DE TIPO RECORD
        typ.tipo = p[3][0]
        typ.atributos = p[3][1]
        parser.ambito_actual.carga_simbolo(typ)

        nuevo_amb = nuevo_amb = ambito(parser.ambito_actual)
        parser.ambito_actual = nuevo_amb

        for e in p[3][1]:
            for ee in e[0]:
                var = variables()
                var.nombre = ee
                if e[1][0].upper() == 'STRING':
                    var.tipo = p[3][0]
                    var.dimension.append(p[3][1])
                else:
                    var.tipo = e[1]
                parser.ambito_actual.carga_simbolo(var)
    else:
        typ.tipo = p[3]
        parser.ambito_actual.carga_simbolo(typ)

        nuevo_amb = nuevo_amb = ambito(parser.ambito_actual)
        parser.ambito_actual = nuevo_amb

def p_type_decl_type(p):
    '''type_decl_type : type_ref
                | TYPE type_ref
                | object_type'''

    if len(p) == 2:
        p[0] = p[1]
    else:
        p[0] = p[2]

def p_type_ref(p):
    '''type_ref : simple_type
                | string_type
                | pointer_type
                | structured_type
                | procedural_type'''

    p[0] = p[1]

def p_simple_type(p):
    '''simple_type : ord_type_name
                | real_type_name
                | variant_type_name
                | simple_type_identifier
                | range_expr DOTDOT range_expr
                | ROUNDOPEN enumeration_id_list ROUNDCLOSE'''

    if len(p) == 2:
        p[0] = p[1]
    elif p[1] == "range_expr":
        p[0] = p[1],p[3]
    else:
        p[0] = p[2]

def p_range_expr(p):
    '''range_expr : range_term
                | range_expr const_addop range_term'''

    if len(p) == 4:
        p[0] = p[1],p[2],p[3]
    else:
        p[0] = p[1]


def p_range_term(p):
    '''range_term : range_factor
                | range_term const_mulop range_factor'''

    if len(p) == 4:
        p[0] = p[1],p[2],p[3]
    else:
        p[0] = p[1]

def p_range_factor(p):
    '''range_factor : identifier
                | unsigned_number
                | sign range_factor
                | literal
                | identifier ROUNDOPEN const_expr ROUNDCLOSE
                | ROUNDOPEN const_expr ROUNDCLOSE'''

    if len(p) == 3:
        p[0] = p[1],p[2]
    elif len(p) == 4:
        p[0] = p[2]
    elif len(p) == 5:
        p[0] = p[1], p[3]
    else:
        p[0] = p[1]

def p_simple_type_identifier(p):
    '''simple_type_identifier : IDENTIFIER
                | unit_name POINT identifier'''

    if len(p) == 4:
        p[0] = p[1],p[3]
    else:
        p[0] = p[1]

def p_enumeration_id_list(p):
    '''enumeration_id_list : enumeration_id COMMA enumeration_id
                | enumeration_id_list COMMA enumeration_id'''

    if type(p[1]) == list:
        p[0] = p[1]+[p[3]]
    else:
        p[0] = [p[1],p[3]]

def p_enumeration_id(p):
    '''enumeration_id : identifier'''

    p[0] = p[1]

def p_pointer_type(p):
    '''pointer_type : DEREF identifier'''

    p[0] = p[2]

def p_structured_type(p):
    '''structured_type : unpacked_structured_type
                | PACKED unpacked_structured_type'''

    if len(p) == 3:
        p[0] = p[2]
    else:
        p[0] = p[1]

def p_unpacked_structured_type(p):
    '''unpacked_structured_type : array_type
                | record_type
                | set_type
                | file_type'''

    p[0] = p[1]

def p_array_type(p):
    '''array_type : ARRAY SQUAREOPEN simple_type_list SQUARECLOSE OF type_ref
                | ARRAY OF type_ref'''

    if len(p) == 4:
        p[0] = p[3]
    else:
        p[0] = p[3],p[6]

def p_simple_type_list(p):
    '''simple_type_list : simple_type
        | simple_type_list COMMA simple_type'''

    if len(p) == 2:
        p[0] = p[1]
    else:
        p[0] = p[1], p[3]

def p_record_type(p):
    '''record_type : RECORD field_list END
                 | RECORD END'''

    if len(p) == 4:
        p[0] = p[1],p[2]
    else:
        p[0] = p[1]

def p_field_list(p):
    '''field_list : fixed_part
                | variant_part
                | fixed_part_2 SEMICOLON variant_part'''

    if len(p) == 4:
        p[0] = p[1],p[3]
    else:
        p[0] = p[1]

def p_fixed_part(p):
    '''fixed_part : fixed_part_2
                | fixed_part_2 SEMICOLON'''

    p[0] = p[1]

def p_fixed_part_2(p):
    '''fixed_part_2 : record_section
                | fixed_part_2 SEMICOLON record_section'''

    if len(p) == 4:
        p[0] = p[1]+[p[3]]
    else:
        p[0] = [p[1]]

def p_record_section(p):
    '''record_section : record_section_id_list COLON type_ref'''

    p[0] = p[1],p[3]

def p_record_section_id_list(p):
    '''record_section_id_list : record_section_id
                | record_section_id_list COMMA record_section_id'''

    if len(p) == 4:
        p[0] = p[1]+[p[3]]
    else:
        p[0] = [p[1]]

def p_record_section_id(p):
    '''record_section_id : identifier'''

    p[0] = p[1]

def p_variant_part(p):
    '''variant_part : CASE tag_field OF variant_list'''

    p[0] = p[2], p[4]

def p_tag_field(p):
    '''tag_field : tag_field_name
                | tag_field_name COLON tag_field_typename'''

    if len(p) == 4:
        p[0] = p[1], p[3]
    else:
        p[0] = p[1]

def p_tag_field_name(p):
    '''tag_field_name : identifier'''

    p[0] = p[1]

def p_tag_field_typename(p):
    '''tag_field_typename : identifier'''

    p[0] = p[1]

def p_variant_list(p):
    '''variant_list : variant_list_2
                | variant_list_2 SEMICOLON'''

    p[0] = p[1]

def p_variant_list_2(p):
    '''variant_list_2 : variant
                | variant_list_2 SEMICOLON variant'''

    if len(p) == 4:
        p[0] = p[1], p[3]
    else:
        p[0] = p[1]

def p_variant(p):
    '''variant : case_tag_list COLON ROUNDOPEN variant_field_list ROUNDCLOSE'''

    p[0] = p[1],p[4]

def p_variant_field_list(p):
    '''variant_field_list :
                | field_list'''

    if len(p) == 2:
        p[0] = p[1]
    else:
        p[0] = None

def p_case_tag_list(p):
    '''case_tag_list : const_expr
                | case_tag_list COMMA const_expr'''

    if len(p) == 4:
        p[0] = p[1], p[3]
    else:
        p[0] = p[1]

def p_set_type(p):
    '''set_type : SET OF simple_type'''

    p[0] = p[3]

def p_file_type(p):
    '''file_type : FILE OF type_ref
                | FILE'''

    if len(p) == 4:
        p[0] = p[3]
    else:
        p[0] = p[1]

# MICHE ########################################################################

def p_string_type(p):
    '''string_type : string_type_name
                | STRING SQUAREOPEN const_expr SQUARECLOSE'''

    if len(p) == 2:
        p[0] = p[1]
    else:
        p[0] = p[1],p[3]

def p_procedural_type(p):
    '''procedural_type : procedural_type_kind
                | procedural_type_kind calling_conventions'''

    if len(p) == 2:
        p[0] = p[1]
    else:
        p[0] = p[1],p[2]

def p_procedural_type_kind(p):
    '''procedural_type_kind : procedural_type_decl
                | procedural_type_decl OF OBJECT'''

    p[0] = p[1]

def p_procedural_type_decl(p):
    '''procedural_type_decl : PROCEDURE fp_list
                | FUNCTION fp_list COLON fptype'''

    if len(p) == 3:
        p[0] = p[2]
    else:
        p[0] = p[2],p[4]

def p_object_type(p):
    '''object_type : new_object_type
                | old_object_type'''

    p[0] = p[1]

def p_old_object_type(p):
    '''old_object_type : OBJECT oot_successor oot_component_list oot_privat_list END'''

    p[0] = p[2],p[3],p[4]

def p_oot_privat_list(p):
    '''oot_privat_list :
                | PRIVATE oot_component_list'''

    if len(p) == 3:
        p[0] = p[2]
    else:
        p[0] = None

def p_oot_component_list(p):
    '''oot_component_list :
                | oot_field_list
                | oot_field_list oot_method_list
                | oot_method_list'''

    if len(p) == 2:
        p[0] = p[1]
    else:
        p[0] = p[1],p[2]

def p_oot_successor(p):
    '''oot_successor : ROUNDOPEN oot_typeidentifier ROUNDCLOSE'''

    p[0] = p[2]

def p_oot_typeidentifier(p):
    '''oot_typeidentifier : identifier'''

    p[0] = p[1]

def p_oot_field_list(p):
    '''oot_field_list : oot_field
                | oot_field_list oot_field'''

    if len(p) == 2:
        p[0] = p[1]
    else:
        p[0] = p[1],p[2]

def p_oot_field(p):
    '''oot_field : oot_id_list COLON type_ref SEMICOLON'''

    p[0] = p[1],p[3]

def p_oot_id_list(p):
    '''oot_id_list : oot_field_identifier
                | oot_id_list COMMA oot_field_identifier'''

    if len(p) == 2:
        p[0] = p[1]
    else:
        p[0] = p[1],p[3]

def p_oot_field_identifier(p):
    '''oot_field_identifier : identifier'''

    p[0] = p[1]

def p_oot_method_list(p):
    '''oot_method_list : oot_method
                | oot_method_list oot_method'''

    if len(p) == 2:
        p[0] = p[1]
    else:
        p[0] = p[1],p[2]

def p_oot_method(p):
    '''oot_method : oot_method_head'''

    p[0] = p[1]

def p_oot_method_head(p):
    '''oot_method_head : proc_heading
                | func_heading
                | oot_constructor_head
                | oot_destructor_head'''

    p[0] = p[1]

def p_oot_constructor_head(p):
    '''oot_constructor_head : CONSTRUCTOR proc_name fp_list fp_directives'''

    p[0] = p[2],p[3],p[4]

def p_oot_destructor_head(p):
    '''oot_destructor_head : DESTRUCTOR proc_name fp_list fp_directives'''

    p[0] = p[2],p[3],p[4]

def p_new_object_type(p):
    '''new_object_type : not_class_reference_type
                | not_object_type
                | not_interface_type'''

    p[0] = p[1]

def p_not_class_reference_type(p):
    '''not_class_reference_type : CLASS OF not_object_type_identifier'''

    p[0] = p[3]

def p_not_object_type_identifier(p):
    '''not_object_type_identifier : identifier'''

    p[0] = p[1]

def p_not_object_type(p):
    '''not_object_type : CLASS
                | CLASS not_heritage
                | CLASS not_component_list_seq END
                | CLASS not_heritage not_component_list_seq END'''

    if len(p) == 2:
        p[0] = p[1]
    elif len(p) == 3:
        p[0] = p[2]
    elif len(p) == 4:
        p[0] = p[2]
    else:
        p[0] = p[2],p[3]

def p_not_interface_type(p):
    '''not_interface_type : INTERFACE
                | INTERFACE not_heritage
                | INTERFACE not_component_list_seq END
                | INTERFACE not_heritage not_component_list_seq END
                | DISPINTERFACE
                | DISPINTERFACE not_heritage
                | DISPINTERFACE not_component_list_seq END
                | DISPINTERFACE not_heritage not_component_list_seq END'''

    if len(p) == 2:
        p[0] = p[1]
    elif len(p) == 3:
        p[0] = p[2]
    elif len(p) == 4:
        p[0] = p[2]
    else:
        p[0] = p[2],p[3]

def p_not_component_list_seq(p):
    '''not_component_list_seq : not_component_list
                | not_component_list_seq visibility_specifier not_component_list'''

    if len(p) == 2:
        p[0] = p[1]
    else:
        p[0] = p[1],p[2],p[3]

def p_not_heritage(p):
    '''not_heritage : ROUNDOPEN not_object_type_identifier_list ROUNDCLOSE'''

    p[0] = p[2]

def p_not_object_type_identifier_list(p):
    '''not_object_type_identifier_list : identifier
                | not_object_type_identifier_list COMMA identifier'''

    if len(p) == 2:
        p[0] = p[1]
    else:
        p[0] = p[1],p[3]

def p_not_component_list(p):
    '''not_component_list : not_guid
                | not_guid not_component_list_1
                | not_guid not_component_list_2
                | not_guid not_component_list_1 not_component_list_2'''

    if len(p) == 2:
        p[0] = p[1]
    elif len(p) == 3:
        p[0] = p[1],p[2]
    else:
        p[0] = p[1],p[2],p[3]

def p_not_guid(p):
    '''not_guid :
                | SQUAREOPEN const_expr SQUARECLOSE'''

    if len(p) == 4:
        p[0] = p[2]
    else:
        p[0] = None

def p_not_component_list_1(p):
    '''not_component_list_1 : not_field_definition
                | not_component_list_1 not_field_definition'''

    if len(p) == 2:
        p[0] = p[1]
    else:
        p[0] = p[1],p[2]

def p_not_component_list_2(p):
    '''not_component_list_2 : not_method_definition
                | not_property_definition
                | not_component_list_2 not_method_definition
                | not_component_list_2 not_property_definition'''

    if len(p) == 2:
        p[0] = p[1]
    else:
        p[0] = p[1],p[2]

def p_not_field_definition(p):
    '''not_field_definition : not_field_identifier_list COLON type_ref SEMICOLON'''

    p[0] = p[1],p[3]

def p_not_field_identifier_list(p):
    '''not_field_identifier_list : not_field_identifier
                | not_field_identifier_list COMMA not_field_identifier'''

    if len(p) == 2:
        p[0] = p[1]
    else:
        p[0] = p[1],p[3]

def p_not_field_identifier(p):
    '''not_field_identifier : identifier'''

    p[0] = p[1]

def p_not_method_definition(p):
    '''not_method_definition : not_method_heading'''

    p[0] = p[1]

def p_not_method_heading(p):
    '''not_method_heading : CLASS proc_heading
                | CLASS func_heading
                | func_heading
                | proc_heading
                | not_constructor_heading
                | not_destructor_heading'''

    if len(p) == 2:
        p[0] = p[1]
    else:
        p[0] = p[2]

def p_not_constructor_heading(p):
    '''not_constructor_heading : CONSTRUCTOR qualified_identifier fp_list fp_directives'''

    p[0] = p[2],p[3],p[4]

def p_not_destructor_heading(p):
    '''not_destructor_heading : DESTRUCTOR qualified_identifier fp_list fp_directives'''

    p[0] = p[2],p[3],p[4]

def p_qualified_identifier(p):
    '''qualified_identifier : identifier
                | visibility_specifier
                | qualified_identifier POINT identifier
                | qualified_identifier POINT visibility_specifier'''

    if len(p) == 2:
        p[0] = p[1]
    else:
        p[0] = p[1],p[3]

def p_not_property_definition(p):
    '''not_property_definition : PROPERTY qualified_identifier not_property_interface not_property_specifiers SEMICOLON not_array_defaultproperty'''

    p[0] = p[2],p[3],p[4],p[6]

def p_not_array_defaultproperty(p):
    '''not_array_defaultproperty :
                | DEFAULT SEMICOLON'''

    if len(p) == 3:
        p[0] = p[1]
    else:
        p[0] = None

def p_not_property_interface(p):
    '''not_property_interface :
                | not_property_parameter_list COLON qualified_identifier not_property_interface_index'''

    if len(p) == 5:
        p[0] = p[1],p[3],p[4]
    else:
        p[0] = None

def p_not_property_interface_index(p):
    '''not_property_interface_index :
                | INDEX expr'''

    if len(p) == 3:
        p[0] = p[2]
    else:
        p[0] = None

def p_not_property_parameter_list(p):
    '''not_property_parameter_list :
                | SQUAREOPEN not_parameter_decl_list SQUARECLOSE'''

    if len(p) == 4:
        p[0] = p[2]
    else:
        p[0] = None

def p_not_parameter_decl_list(p):
    '''not_parameter_decl_list : not_parameter_decl
                | not_parameter_decl_list SEMICOLON not_parameter_decl'''

    if len(p) == 2:
        p[0] = p[1]
    else:
        p[0] = p[1],p[3]

def p_not_parameter_decl(p):
    '''not_parameter_decl : not_parameter_name_list COLON fptype
                | CONST not_parameter_name_list COLON fptype
                | VAR not_parameter_name_list COLON fptype
                | OUT not_parameter_name_list COLON fptype'''

    if len(p) == 4:
        p[0] = p[1],p[3]
    else:
        p[0] = p[2],p[4]

def p_not_parameter_name_list(p):
    '''not_parameter_name_list : not_parameter_name
                | not_parameter_name_list COMMA not_parameter_name'''

    if len(p) == 2:
        p[0] = p[1]
    else:
        p[0] = p[1],p[3]

def p_not_parameter_name(p):
    '''not_parameter_name : identifier'''

    p[0] = p[1]

def p_not_property_specifiers(p):
    '''not_property_specifiers :
                | READONLY not_property_specifiers
                | WRITEONLY not_property_specifiers
                | DISPID const_expr not_property_specifiers
                | DEFAULT const_expr not_property_specifiers
                | NODEFAULT not_property_specifiers
                | STORED const_expr not_property_specifiers
                | READ expr not_property_specifiers
                | IMPLEMENTS not_object_type_identifier_list not_property_specifiers
                | WRITE expr not_property_specifiers'''

    if len(p) == 3:
        p[0] = p[2]
    elif len(p) == 4:
        p[0] = p[2],p[3]
    else:
        p[0] = None

def p_var_decl(p):
    '''var_decl : var_name_list COLON type_ref absolute_clause SEMICOLON
                | var_name_list COLON type_ref absolute_clause EQUAL var_init_value SEMICOLON
                | var_name_list error error_recovery'''

    if len(p) == 6:
        p[0] = p[1],p[3],p[4]
    elif len(p) == 4:
        p[0] = p[1],p[2],p[3]
    else:
        p[0] = p[1],p[3],p[4],p[6]

    for x in p[1]:
        variab = variables()
        variab.nombre = x
        if p[3][0].upper() == 'STRING':
            variab.tipo = p[3][0]
            variab.dimension.append(p[3][1])
        else:
            variab.tipo = p[3]
        parser.ambito_actual.carga_simbolo(variab)

def p_var_init_value(p):
    '''var_init_value : typed_const'''

    p[0] = p[1]

def p_var_name_list(p):
    '''var_name_list : var_name
                | var_name_list COMMA var_name'''

    if len(p) == 2:
        p[0] = [p[1]]
    else:
        p[0] = p[1] + [p[3]]

def p_var_name(p):
    '''var_name : identifier'''

    p[0] = p[1]

def p_absolute_clause(p):
    '''absolute_clause :
                | ABSOLUTE INT COLON INT
                | ABSOLUTE HEX COLON HEX
                | ABSOLUTE declared_var_name'''

    if len(p) == 3:
        p[0] = p[2]
    elif len(p) == 5:
        p[0] = p[2],p[4]
    else:
        p[0] = None

def p_declared_var_name(p):
    '''declared_var_name : identifier'''

    p[0] = p[1]

def p_constructor_decl(p):
    '''constructor_decl : not_constructor_heading not_constructor_block_decl'''

    p[0] = p[1],p[2]

def p_destructor_decl(p):
    '''destructor_decl : not_destructor_heading not_constructor_block_decl'''

    p[0] = p[1],p[2]

def p_not_constructor_block_decl(p):
    '''not_constructor_block_decl : block
                | external_directr
                | asm_block'''

    p[0] = p[1]

def p_proc_decl(p):
    '''proc_decl : proc_heading proc_block
                | CLASS proc_heading proc_block'''

    if len(p) == 3:
        p[0] = p[1],p[2]
    else:
        p[0] = p[2],p[3]

    parser.ambito_actual = parser.ambito_actual.padre   #Subimos al padre

def p_func_decl(p):
    '''func_decl : func_heading func_block
                | CLASS func_heading func_block'''

    if len(p) == 3:
        p[0] = p[1],p[2]
    else:
        p[0] = p[2],p[3]

    parser.ambito_actual = parser.ambito_actual.padre

def p_proc_heading(p):
    '''proc_heading : PROCEDURE proc_name fp_list fp_directives
                | PROCEDURE proc_name EQUAL qualified_identifier SEMICOLON
                | PROCEDURE proc_name error error_recovery'''
    if len(p) == 5:
        p[0] = p[2],p[3],p[4]
    else:
        p[0] = p[2],p[4]

    proc = procedure()
    proc.nombre = p[2]
    proc.parametros = p[3]
    parser.ambito_actual.carga_simbolo(proc)

    nuevo_amb = ambito(parser.ambito_actual)
    parser.ambito_actual = nuevo_amb

    if p[3] <> None:
        for e in p[3]:
            for ee in e[0]:
                var = variables()
                var.nombre = ee
                var.tipo = e[1]
                parser.ambito_actual.carga_simbolo(var)

def p_proc_name(p):
    '''proc_name : identifier
                | visibility_specifier
                | proc_name POINT identifier
                | proc_name POINT visibility_specifier'''

    if len(p) == 2:
        p[0] = p[1]
    else:
        p[0] = p[1],p[3]

def p_func_heading(p):
    '''func_heading : FUNCTION func_name fp_list COLON fptype fp_directives
                | FUNCTION func_name fp_directives
                | FUNCTION func_name EQUAL qualified_identifier SEMICOLON
                | FUNCTION func_name error error_recovery'''

    if len(p) == 7:
        p[0] = p[2],p[3],p[5],p[6]
    elif len(p) == 4:
        p[0] = p[2],p[3]
    elif len(p) == 6:
        p[0] = p[2],p[4]
    else:
        p[0] = p[2],p[3],p[4]

    func = function()
    func.nombre = p[2]
    func.parametros = p[3]
    func.retorno = p[5]
    parser.ambito_actual.carga_simbolo(func)

    nuevo_amb = ambito(parser.ambito_actual)
    parser.ambito_actual = nuevo_amb

    if p[3] <> None:
        for e in p[3]:
            for ee in e[0]:
                var = variables()
                var.nombre = ee
                var.tipo = e[1]
                parser.ambito_actual.carga_simbolo(var)

    #parser.ambito_actual.imprimir()

def p_fp_directives(p):
    '''fp_directives : SEMICOLON
                | SEMICOLON method_access_directive fp_directives
                | SEMICOLON calling_conventions fp_directives
                | SEMICOLON DISPID const_expr fp_directives
                | SEMICOLON MESSAGE integer_const fp_directives'''

    if len(p) == 2:
        p[0] = p[1]
    elif len(p) == 4:
        p[0] = p[2],p[3]
    else:
        p[0] = p[3],p[4]

     #NO trabajamos directives

def p_func_name(p):
    '''func_name : identifier
                | visibility_specifier
                | func_name POINT identifier
                | func_name POINT visibility_specifier'''

    if len(p) == 2:
        p[0] = p[1]
    else:
        p[0] = p[1],p[3]

def p_proc_block(p):
    '''proc_block : proc_block_decl
                | inline_directr SEMICOLON'''

    p[0] = p[1]

def p_func_block(p):
    '''func_block : proc_block_decl
                | inline_directr SEMICOLON'''

    p[0] = p[1]

def p_proc_block_decl(p):
    '''proc_block_decl : block
                | external_directr
                | asm_block
                | FORWARD SEMICOLON'''

    p[0] = p[1]
    parser.ambito_actual.imprimir() # Imprime las declaraciones

###NICOF###


def p_external_directr(p):
    '''external_directr : EXTERNAL SEMICOLON
                | EXTERNAL identifier external_directr_2
                | EXTERNAL literal external_directr_2
                | EXTERNAL literal external_directr_2 SEMICOLON
                | EXTERNAL identifier external_directr_2 SEMICOLON'''

    if len(p) == 3:
        p[0] = p[1],p[2]
    else:
        p[0] = p[2],p[3]


def p_external_directr_2(p):
    '''external_directr_2 :
                | NAME identifier
                | NAME literal
                | INDEX external_directr_3'''

    if len(p) == 3:
        p[0] = p[2]
    else:
        p[0] = None

def p_external_directr_3(p):
    '''external_directr_3 : INT
                | MINUS INT'''

    if len(p) == 3:
        p[0] = p[1],p[2]
    else:
        p[0] = p[1]

def p_asm_block(p):
    '''asm_block : impl_decl_sect_list ASMBODY SEMICOLON'''

    p[0] = p[1]

def p_block(p):
    '''block : impl_decl_sect_list compound_stmt SEMICOLON'''

    p[0] = p[1],p[2]

def p_fp_list(p):   #Parametros
    '''fp_list :
                | ROUNDOPEN fp_sect_list ROUNDCLOSE'''
    if len(p) == 4:
        p[0] = p[2]
    else:
        p[0] = None


##    for e in p[2]:
##        if e != None:
##            for ee in e[0]:
##                varaux = variables()    #Usamos una aux para pasarle a la funcion
##                varaux.nombre = ee
##                varaux.tipo = e[1][0]
##                if parser.ambito_actual.existe_tipo(varaux):
##                    var = varaux
##                else:
##                    break  #Si no existe el tipo no copiamos varaux


def p_fp_sect_list(p):
    '''fp_sect_list :
                | fp_sect
                | fp_sect_list SEMICOLON fp_sect'''

    if len(p) == 2:
        p[0] = [p[1]]
    elif len(p) == 4:
        p[0] = p[1]+[p[3]]
    else:
        p[0] = None

def p_fp_sect(p):    #Adentro de los parametros, donde se pone variable : tipo
    '''fp_sect : param_name_list COLON fptype_new
                | param_name_list
                | VAR param_name_list COLON fptype_new
                | VAR param_name_list
                | OUT param_name_list COLON fptype_new
                | OUT param_name_list
                | CONST param_name_list COLON fptype_new
                | CONST param_name_list'''

    if len(p) == 4:
##        varaux = variables()
##        varaux.tipo = p[3][0]
##        if parser.ambito_actual.existe_tipo(varaux):    #Llevamos la variable hacia arriba si existe el tipo
            p[0] = p[1],p[3]
    elif len(p) == 2:
        p[0] = p[1]
    elif len(p) == 5:
        p[0] = p[2],p[4]
    else:
        p[0] = p[2]

def p_param_name_list(p):       #Parametros
    '''param_name_list : param_name
                | param_name_list COMMA param_name'''

    if len(p) == 2:
        p[0] = [p[1]]
    else:
        p[0] = p[1]+[p[3]]

def p_param_name(p):
    '''param_name : identifier'''

    p[0] = p[1]

def p_fptype(p):
    '''fptype : identifier
                | unit_name POINT identifier'''

    if len(p) == 2:
        p[0] = p[1]
    else:
        p[0] = p[1],p[3]

def p_fptype_new(p):
    '''fptype_new : fptype
                | fptype EQUAL const_expr
                | ARRAY OF fptype
                | ARRAY OF CONST'''

    if len(p) == 2:
        p[0] = p[1]
    elif p[1]=="fptype":
        p[0] = p[1],p[3]
    elif p[3] =="fptype":
        p[0] = p[3]
    else:
        p[0] = p[1],p[2],p[3]

def p_stmt(p):
    '''stmt : unlabelled_stmt
                | label_name COLON unlabelled_stmt'''

    if len(p) == 2:
        p[0] = p[1]
    else:
        pass                        #No usamos LABEL

def p_unlabelled_stmt(p):   #Asignaciones de variables, llamadas,...
    '''unlabelled_stmt :
                | assignment
                | proc_call
                | goto_stmt
                | compound_stmt
                | if_stmt
                | case_stmt
                | repeat_stmt
                | while_stmt
                | for_stmt
                | with_stmt
                | asm_stmt
                | inline_directr
                | inherited_message
                | try_stmt
                | raise_stmt'''

    if len(p) == 2:
        p[0] = p[1]
    else:
        p[0] = None

def p_assignment(p):    #Asignacion de variable
    '''assignment : var_reference ASSIGN expr'''

    if parser.ambito_actual.busca_simbolo(p[1]):
        p[0] = p[1],p[3]

def p_proc_call(p):   #pro_call guarda el nombre del procedimiento y el/los parametros
    '''proc_call : var_reference'''

    p[0] = p[1]

    parser.ambito_actual.busca_simbolo(p[0][0])

def p_goto_stmt(p):
    '''goto_stmt : GOTO label_name'''

    pass

def p_compound_stmt(p):
    '''compound_stmt : BEGIN stmt_list END'''   #Empieza el bloque ppal o bloque de proc,...

    p[0] = p[2]

def p_stmt_list(p):
    '''stmt_list : stmt
                | stmt_list SEMICOLON stmt'''

    if len(p) == 2:
        p[0] = p[1]
    else:
        p[0] = p[1],p[3]

def p_if_stmt(p):
    '''if_stmt : IF expr if_then_else_branch'''

    p[0] = p[2],p[3]

def p_if_then_else_branch(p):
    '''if_then_else_branch : THEN then_branch
                | THEN then_branch ELSE else_branch'''

    if len(p) == 3:
        p[0] = p[2]
    else:
        p[0] = p[2],p[4]

def p_then_branch(p):
    '''then_branch : stmt'''

    p[0] = p[1]

def p_else_branch(p):
    '''else_branch : stmt'''

    p[0] = p[1]

def p_case_stmt(p):
    '''case_stmt : CASE expr OF case_list else_case END'''

    p[0] = p[2],p[4],p[5]

def p_case_list(p):
    '''case_list : case_item
                | case_list SEMICOLON case_item'''

    if len(p) == 2:
        p[0] = p[1]
    else:
        p[0] = p[1],p[3]

def p_case_item(p):
    '''case_item :
                | case_label_list COLON stmt'''

    if len(p) == 4:
        p[0] = p[1],p[3]
    else:
         p[0] = None

def p_case_label_list(p):
    '''case_label_list : case_label
                | case_label_list COMMA case_label'''

    if len(p) == 2:
         p[0] = p[1]
    else:
         p[0] = p[1],p[3]

def p_case_label(p):
    '''case_label : const_expr
                | const_expr DOTDOT const_expr'''

    if len(p) == 2:
         p[0] = p[1]
    else:
         p[0] = p[1],p[3]

def p_else_case(p):
    '''else_case :
                | ELSE stmt_list'''

    if len(p) == 3:
        p[0] = p[2]
    else:
        p[0] = None

def p_repeat_stmt(p):
    '''repeat_stmt : REPEAT stmt_list UNTIL expr'''

    p[0] = p[2],p[4]

def p_while_stmt(p):
    '''while_stmt : WHILE expr DO stmt'''

    p[0] = p[2],p[4]

def p_for_stmt(p):
    '''for_stmt : FOR identifier ASSIGN expr for_to'''

    p[0] = p[2],p[4],p[5]

def p_for_to(p):
    '''for_to : TO expr DO stmt
                | DOWNTO expr DO stmt'''

    p[0] = p[2],p[4]

def p_with_stmt(p):
    '''with_stmt : WITH expr_list DO stmt'''

    p[0] = p[2],p[4]

def p_inherited_message(p):
    '''inherited_message : INHERITED'''

    p[0] = p[1]

def p_try_stmt(p):
    '''try_stmt : TRY stmt_list try_handler'''

    p[0] = p[2],p[3]

def p_try_handler(p):
    '''try_handler : FINALLY stmt_list END
                | EXCEPT exception_block END'''

    p[0] = p[2]

def p_exception_block(p):
    '''exception_block : exception_handler_list exception_block_else_branch
                | exception_handler_list SEMICOLON exception_block_else_branch
                | stmt_list'''

    if len(p) == 3:
        p[0] = p[1],p[2]
    elif len(p) == 4:
        p[0] = p[1],p[3]
    else:
        p[0] = p[1]

def p_exception_handler_list(p):
    '''exception_handler_list : exception_handler
                | exception_handler_list SEMICOLON exception_handler'''

    if len(p) == 2:
        p[0] = p[1]
    else:
        p[0] = p[1],p[3]

def p_exception_block_else_branch(p):
    '''exception_block_else_branch :
                | ELSE stmt_list'''

    if len(p) == 3:
        p[0] = p[2]
    else:
        p[0] = None

def p_exception_handler(p):
    '''exception_handler : ON exception_identifier DO stmt'''

    p[0] = p[2],p[4]

def p_exception_identifier(p):
    '''exception_identifier : exception_class_type_identifier
                | exception_variable COLON exception_class_type_identifier'''

    if len(p) == 2:
        p[0] = p[1]
    else:
        p[0] = p[1],p[3]

def p_exception_class_type_identifier(p):
    '''exception_class_type_identifier : identifier'''

    p[0] = p[1]

def p_exception_variable(p):
    '''exception_variable : identifier'''

    p[0] = p[1]

def p_raise_stmt(p):
    '''raise_stmt : RAISE
                | RAISE expr
                | RAISE expr AT expr'''

    if len(p) == 3:
        p[0] = p[2]
    elif len(p) == 5:
        p[0] = p[2],p[4]
    else:
        p[0] = p[1]

def p_asm_stmt(p):
    '''asm_stmt : ASMBODY'''

    pass

def p_expr_list(p):
    '''expr_list : expr
                | expr_list COMMA expr'''

    if len(p) == 2:
        p[0] = [p[1]]
    else:
        p[0] = p[1] + [p[3]]

def p_expr(p):
    '''expr : simple_expr
                | simple_expr relop expr
                | simple_expr COLON simple_expr
                | simple_expr COLON simple_expr COLON simple_expr'''

    if len(p) == 2:
        p[0] = p[1]
    elif len(p) == 6:
        p[0] = p[1],p[3],p[5]
    elif p[2] == 'relop':
        p[0] = p[1],p[2],p[3]
    else:
        p[0] = p[1],p[3]

def p_relop(p):
    '''relop : EQUAL
                | NOTEQUAL
                | LOWER
                | GREATER
                | LOWEREQUAL
                | GREATEREQUAL
                | IN
                | IS'''

    p[0] = p[1]

def p_simple_expr(p):
    '''simple_expr : term
                | simple_expr addop term'''         #term + term    ->  3 + 2    addop = '+'

    if len(p) == 2:
        p[0] = p[1]
    else:
        p[0] = p[1],p[2],p[3]

def p_addop(p):
    '''addop : PLUS
                | MINUS
                | OR
                | XOR'''

    p[0] = p[1]

def p_term(p):
    '''term : factor
                | term mulop factor'''

    if len(p) == 2:
        p[0] = p[1]
    else:
        p[0] = p[1],p[2],p[3]

def p_mulop(p):
    '''mulop : STAR
                | SLASH
                | DIV
                | MOD
                | SHL
                | SHR
                | AND
                | AS'''

    p[0] = p[1]

def p_factor(p):                                #unsigned.. trae un numero
    '''factor : unsigned_number
                | literal
                | NIL
                | SQUAREOPEN elem_list SQUARECLOSE
                | NOT factor
                | sign factor
                | DEREF factor
                | var_reference'''

    if len(p) == 2:
        p[0] = p[1]
    elif len(p) == 4:
        p[0] = p[2]
    elif p[1] == 'sign':
        p[0] = p[1],p[2]
    else:
        p[0] = p[2]

def p_var_reference(p):
    '''var_reference : var_address variable
                | variable'''

    if len(p) == 2:
        p[0] = p[1]
    else:
        pass

def p_var_address(p):
    '''var_address : ADDRESSOF
                | var_address ADDRESSOF'''

    pass

def p_variable(p):              #Asignacion de una variable a otra    c := d
    '''variable : identifier
                | INHERITED identifier
                | ROUNDOPEN expr ROUNDCLOSE
                | ROUNDOPEN ROUNDCLOSE
                | variable var_specifiers'''

    if len(p) == 2:
        p[0] = p[1]
    elif len(p) == 4:
        p[0] = p[2]
    elif p[2] == 'identifier':
        p[0] = p[2]
    else:
        p[0] = p[1],p[2]

def p_var_specifiers(p):        #Parametros de la llamada de procedimiento, controlamos que existan las variables
    '''var_specifiers : SQUAREOPEN expr_list SQUARECLOSE
                | SQUAREOPEN SQUARECLOSE
                | ROUNDOPEN expr_list ROUNDCLOSE
                | ROUNDOPEN ROUNDCLOSE
                | POINT identifier
                | DEREF'''

    if len(p) == 2:
        p[0] = p[1]
    elif len(p) == 4:
        p[0] = p[2]
    elif p[2] == 'identifier':
        p[0] = p[2]
    else:
        p[0] = p[1],p[2]

    for x in p[0]:
        parser.ambito_actual.busca_simbolo(x)

def p_elem_list(p):
    '''elem_list :
                | elem_list1'''

    if len(p) == 2:
        p[0] = p[1]
    else:
        p[0] = None

def p_elem_list1(p):
    '''elem_list1 : elem
                | elem_list1 COMMA elem'''

    if len(p) == 2:
        p[0] = p[1]
    else:
        p[0] = p[1],p[3]

def p_elem(p):
    '''elem : expr
                | expr DOTDOT expr'''

    if len(p) == 2:
        p[0] = p[1]
    else:
        p[0] = p[1],p[3]

def p_inline_directr(p):
    '''inline_directr : INLINE ROUNDOPEN inline_element ROUNDCLOSE'''

    pass

def p_inline_element(p):
    '''inline_element : inline_param
                | inline_element SLASH inline_param'''

    pass

def p_inline_param(p):
    '''inline_param : LOWER inline_const
                | GREATER inline_const
                | inline_param_variable'''

    pass

def p_inline_param_variable(p):
    '''inline_param_variable : var_reference
                | inline_param_variable sign inline_const
                | HEX'''

    pass

def p_inline_const(p):
    '''inline_const : const_factor'''

    pass

def p_literal(p):
    '''literal : STRINGLITERAL
                | ASCIICHAR
                | STRINGLITERAL literal
                | ASCIICHAR literal'''

    if len(p) == 2:
        p[0] = p[1]
    else:
        p[0] = p[2]

def p_integer_const(p):
    '''integer_const : sign INT
                | INT
                | sign HEX
                | HEX
                | identifier
                | sign identifier'''

    if len(p) == 2:
        p[0] = p[1]
    elif p[2] == 'identifier':
        p[0] = p[1],p[2]
    else:
        p[0] = p[1]

def p_real_type_name(p):
    '''real_type_name : REAL48
                | REAL
                | SINGLE
                | DOUBLE
                | EXTENDED
                | CURRENCY
                | COMP'''

    p[0] = p[1]

def p_ord_type_name(p):
    '''ord_type_name : SHORTINT
                | SMALLINT
                | ORDINTEGER
                | BYTE
                | LONGINT
                | INTEGER
                | INT64
                | WORD
                | BOOLEAN
                | CHAR
                | LONGWORD
                | CARDINAL'''

    p[0] = p[1]

def p_variant_type_name(p):
    '''variant_type_name : VARIANT
                | OLEVARIANT'''

    p[0] = p[1]

def p_string_type_name(p):
    '''string_type_name : STRING'''

    p[0] = p[1]

def p_calling_conventions(p):
    '''calling_conventions : REGISTER
                | PASCAL
                | CDECL
                | SAFECALL
                | NEAR
                | FAR
                | STDCALL
                | RESIDENT
                | EXPORT
                | ASSEMBLER'''

    p[0] = p[1]

def p_property_specifier_directives(p):
    '''property_specifier_directives : DEFAULT
                | READ
                | WRITE
                | STORED
                | NODEFAULT
                | IMPLEMENTS
                | WRITEONLY
                | READONLY
                | DISPID'''

    p[0] = p[1]

def p_method_access_directive(p):
    '''method_access_directive : ABSTRACT
                | AUTOMATED
                | DYNAMIC
                | OVERLOAD
                | OVERRIDE
                | REINTRODUCE
                | VIRTUAL'''

    p[0] = p[1]

def p_non_reserved(p):
    '''non_reserved : AT
                | ABSOLUTE
                | ON
                | NAME
                | INDEX
                | MESSAGE
                | CONTAINS
                | REQUIRES
                | FORWARD
                | OUT'''

    p[0] = p[1]

def p_visibility_specifier(p):
    '''visibility_specifier : PUBLISHED
                | PUBLIC
                | PROTECTED
                | PRIVATE'''

    p[0] = p[1]

def p_other(p):
    '''other : PACKAGE
                | UNIT
                | LIBRARY
                | PROGRAM
                | EXTERNAL'''

    p[0] = p[1]

def p_identifier(p):
    '''identifier : IDENTIFIER
                | real_type_name
                | ord_type_name
                | variant_type_name
                | string_type_name
                | method_access_directive
                | calling_conventions
                | property_specifier_directives
                | non_reserved
                | other'''

    p[0] = p[1]

def p_error(p):
    print 'Error en: %s'%p

parser = yacc.yacc()
parser.ambito_actual = ambito(None)
####TITO####

if __name__ == '__main__':
    if os.path.exists("Errores.txt"): os.remove("Errores.txt")  #Si existe el listado de errores lo borra
    datos = ''.join(open("EjemploPT.pas").readlines())
    parser.parse(datos)
    parser.ambito_actual.imprimir()